#include "src\mainwindow.h"
#include "src\procthread.h"

#include <QPixmap>
#include <QPainter>
#include <QScrollArea>

#include <windows.h>

MainWindow::MainWindow(QWidget *parent)
        : QMainWindow(parent), ui(new Ui::MainWindow) {
    ui->setupUi(this);

    // 设置窗体名称
    setWindowTitle(tr("数据结构与算法实验-迷宫问题的对比试验研究-可视化图形界面"));
    // 设置窗体图标
    this->setWindowIcon(QIcon(":/img/guet.ico"));

    ui->msgList->addItem(tr("使用说明："));
    ui->msgList->addItem(tr("1. 首先设置迷宫的行数和列数"));
    ui->msgList->addItem(tr("2. 其次点击“功能按键“区的“生成迷宫数据”按钮"));
    ui->msgList->addItem(tr("3. 然后点击“功能按键“区的“开始寻找路径”按钮"));
    ui->msgList->addItem(tr("4. 最后观察可视化界面动画输出以及“信息输出区“的相关信息"));
    ui->msgList->addItem(tr("”随机权值“说明："));
    ui->msgList->addItem(tr("1. 开启后，从一个方块移动到另一个方块就需要一定的花费"));
    ui->msgList->addItem(tr("2. 未开始时：方块移动到另一个方块的花费默认为1，寻找最短路算法（dijkstra)将会退化为bfs"));
    ui->msgList->addItem(tr("   开启后：方块移动到另一个方块的花费为随机值，寻找最短路算法（a*、dijkstra）给出的解为花费最少的解，而不是经过最少的方块的解"));
    ui->msgList->addItem(tr("注： "));
    ui->msgList->addItem(
            tr("1. 当迷宫过大时，可视化界面会过大，此时可以右上角“最大化”后拖动滑动条观察可视化界面动画输出"));
    ui->msgList->addItem(tr("2. 因为可视化界面动画效果，因此最终输出的某算法用时是与直观可视化时的时间不同的"));

    // 设置可视化窗口大小
    ui->scrollArea->setWidget(ui->mazes);
    ui->scrollArea->setMinimumSize((9 * default_squareSize + 10 * default_interval) * 2 + default_squareSize * 6,
                                   (9 * default_squareSize + 10 * default_interval) * 2 + default_squareSize * 6);

    // 创建生成随机数据线程
    SpawnDataThread *spawnDataThread = new SpawnDataThread;

    //创建算法执行线程
    AStarProcThread *aStarProcThread = new AStarProcThread;
    BfsProcThread *bfsProcThread = new BfsProcThread;
    DfsProcThread *dfsProcThread = new DfsProcThread;
    DijkstraProcThread *dijkstraProcThread = new DijkstraProcThread;


    connect(this, &MainWindow::spawnData, spawnDataThread, &SpawnDataThread::receiveSize);
    // 启动生成随机数据线程
    connect(ui->spawnData, &QPushButton::clicked, this, [=]() {
        m_n = ui->n->value();
        m_m = ui->m->value();
        emit spawnData(m_n, m_m, ui->isEdge->isChecked());
        spawnDataThread->start();
    });

    connect(this, &MainWindow::sendSize, aStarProcThread, &AStarProcThread::receiveSize);
    connect(this, &MainWindow::sendSize, bfsProcThread, &BfsProcThread::receiveSize);
    connect(this, &MainWindow::sendSize, dfsProcThread, &DfsProcThread::receiveSize);
    connect(this, &MainWindow::sendSize, dijkstraProcThread, &DijkstraProcThread::receiveSize);
    // 接收子线程发送的数据
    connect(spawnDataThread, &SpawnDataThread::sendSuccessful, this, [=](bool value) {
        //终止算法执行线程
        aStarProcThread->terminate();
        bfsProcThread->terminate();
        dfsProcThread->terminate();
        dijkstraProcThread->terminate();

        //检查数据是否生成成功
        isSpawnDataSuccessful = value;
        string info("生成迷宫");
        info.append(to_string(m_n)).append("*").append(to_string(m_m));
        if (!isSpawnDataSuccessful)
            info.append("数据失败！");
        else
            info.append("数据成功！");
        ui->msgList->addItem(tr(info.c_str()));

        // 根据迷宫尺寸调整图形化时的大小
        squareSize = default_squareSize;
        interval = default_interval;
        if (m_n > 10 || m_m > 10) {
            squareSize -= m_n / 10;
            interval -= (m_n / 10) / 2;
            squareSize = max(1, squareSize);
            interval = max(1, interval);
        }
        squareQSize = QSize(squareSize, squareSize);

        emit sendSize(squareSize, interval);

        // 画布大小
        int height = m_n * squareSize + (m_n + 1) * interval;
        int width = m_m * squareSize + (m_m + 1) * interval;
        QSize size(width, height);
        ui->mazes->setMinimumSize(width * 2 + squareSize * 3, height * 2 + squareSize * 3);

        // 创建图形化绘制工具
        pixmapAStar = new QPixmap(size);
        pixmapAStar->fill(Qt::gray);
        painterAStar = new QPainter(pixmapAStar);

        ui->astar->setMinimumSize(size);
        ui->astar->setMaximumSize(size);
        ui->astar->setPixmap(*pixmapAStar);

        pixmapDfs = new QPixmap(size);
        pixmapDfs->fill(Qt::gray);
        painterDfs = new QPainter(pixmapDfs);

        ui->dfs->setMinimumSize(size);
        ui->dfs->setMaximumSize(size);
        ui->dfs->setPixmap(*pixmapDfs);

        pixmapBfs = new QPixmap(size);
        pixmapBfs->fill(Qt::gray);
        painterBfs = new QPainter(pixmapBfs);

        ui->bfs->setMinimumSize(size);
        ui->bfs->setMaximumSize(size);
        ui->bfs->setPixmap(*pixmapBfs);

        pixmapDijkstra = new QPixmap(size);
        pixmapDijkstra->fill(Qt::gray);
        painterDijkstra = new QPainter(pixmapDijkstra);

        ui->dijkstra->setMinimumSize(size);
        ui->dijkstra->setMaximumSize(size);
        ui->dijkstra->setPixmap(*pixmapDijkstra);

    });

    connect(ui->startSeek, &QPushButton::clicked, this, [=]() {
        ui->msgList->addItem("开始执行算法......");
        aStarProcThread->start();
        bfsProcThread->start();
        dfsProcThread->start();
        dijkstraProcThread->start();
    });

    connect(aStarProcThread, &AStarProcThread::sendSpentTime, this, [=](long long time) {
        string str("A*算法已完成！用时：");
        str.append(to_string(time / 1000.0)).append(" 秒");
        ui->msgList->addItem(tr(str.c_str()));
    });

    connect(bfsProcThread, &BfsProcThread::sendSpentTime, this, [=](long long time) {
        string str("广度优先搜索Bfs算法已完成！用时：");
        str.append(to_string(time / 1000.0)).append(" 秒");
        ui->msgList->addItem(tr(str.c_str()));
    });

    connect(dfsProcThread, &DfsProcThread::sendSpentTime, this, [=](long long time) {
        string str("深度优先搜索Dfs算法已完成！用时：");
        str.append(to_string(time / 1000.0)).append(" 秒");
        ui->msgList->addItem(tr(str.c_str()));
    });

    connect(dijkstraProcThread, &DijkstraProcThread::sendSpentTime, this, [=](long long time) {
        string str("Dijkstra算法已完成！用时：");
        str.append(to_string(time / 1000.0)).append(" 秒");
        ui->msgList->addItem(tr(str.c_str()));
    });

//不能把空指针传入connect
//比如AStarProcThread *aStarProcThread;
//之后并没有 aStarProcThread = new 比如AStarProcThread()
//这样是不能把aStarProcThread传入的
    connect(aStarProcThread, &AStarProcThread::drawProc, this, [=](int type, int x, int y, const QColor &color) {
        if (type == 0) {
            drawProcByRC(ui->astar, pixmapAStar, painterAStar, x, y, color);
        } else {
            drawProcByP(ui->astar, pixmapAStar, painterAStar, x, y, color);
        }
    });

    connect(bfsProcThread, &BfsProcThread::drawProc, this, [=](int type, int x, int y, const QColor &color) {
        if (type == 0) {
            drawProcByRC(ui->bfs, pixmapBfs, painterBfs, x, y, color);
        } else {
            drawProcByP(ui->bfs, pixmapBfs, painterBfs, x, y, color);
        }
    });

    connect(dfsProcThread, &DfsProcThread::drawProc, this, [=](int type, int x, int y, const QColor &color) {
        if (type == 0) {
            drawProcByRC(ui->dfs, pixmapDfs, painterDfs, x, y, color);
        } else {
            drawProcByP(ui->dfs, pixmapDfs, painterDfs, x, y, color);
        }
    });

    connect(dijkstraProcThread, &DijkstraProcThread::drawProc, this, [=](int type, int x, int y, const QColor &color) {
        if (type == 0) {
            drawProcByRC(ui->dijkstra, pixmapDijkstra, painterDijkstra, x, y, color);
        } else {
            drawProcByP(ui->dijkstra, pixmapDijkstra, painterDijkstra, x, y, color);
        }
    });
}

MainWindow::~MainWindow() {
    delete ui;
}

void
MainWindow::drawProcByRC(QLabel *label, QPixmap *pixmap, QPainter *painter, int row, int col, const QColor &color) {
    painter->setPen(color);
    painter->setBrush(color);
    // 根据大小和位置设置画布上的位置
    painter->drawRect(QRect(QPoint(row * squareSize + interval * (row + 1), col * squareSize + interval * (col + 1)),
                            squareQSize));
    label->setPixmap(*pixmap);
}

void MainWindow::drawProcByP(QLabel *label, QPixmap *pixmap, QPainter *painter, int px, int py, const QColor &color) {
    painter->setPen(color);
    painter->setBrush(color);
    // 根据大小和位置设置画布上的位置
    painter->drawRect(QRect(QPoint(px, py), squareQSize));
    label->setPixmap(*pixmap);
}